/*****************************************************************************
 * 
 * Copyright [2013] [Mervin.Wong]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 *       
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *  
 *****************************************************************************/
package util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import util.ExtractIpLink.ExtractContent;

/**
 *@name ExtractIpLink.java
 *@description 提取一个链路上的逐跳IP地址
 *@author 王进法<Mervin.Wong>
 *@version 0.1.0
 *@date 2013-1-11下午3:26:17
 */
/****************************************************************************************
 * 使用步骤：
 * 1，对于从CAIDA上下载的数据包，解压后会得到一个XXX.warts文件，
 * 2，使用sc_wartscat 将多个文件合并，命令：sc_wartscat 文件名1 文件名2 -o 输出文件的文件名。此步骤可选
 * 3，sc_analysis_dump XXX.wrats > out.txt 将上一步生成的文件转换成文本格式并提取出想要的内容.sc_analysis_dump有多个参数可选择
 * 4，使用本程序开提取所需要的信息。例如，	ExtractIpLink ipLink = new ExtractIpLink("./data/ip.txt","./data/ips.txt",ExtractIpLink.extractContent.LINK_ALL);ipLink.run();
 ***************************************************************************************/
public class ExtractIpLink{
	private String fileName = null; //文件名
	private String savePath = null;//保存的文件名
	//提起内容内容的格式
	public enum ExtractContent {SOURCE_DESTINATION, LINK_I, LINK_C, LINK_ALL, LINK_I_RTT, LINK_C_RTT,LINK_ALL_RTT,LINK_I_TRY, LINK_C_TRY, LINK_ALL_TRY};
	private ExtractContent extractContentValue = null;
	/**
	 * @description 初始化
	 * @param String fileName
	 * @param String savePath 
	 * 
	 */
	public ExtractIpLink(String fileName, String savePath){
		this.fileName = fileName;
		this.savePath = savePath;
		this.extractContentValue = ExtractContent.SOURCE_DESTINATION;
	}
	/**
	 * @description 初始化
	 * @param String fileName
	 * @param String savePath 
	 * @param extractContent extractContentValue
	 */
	public ExtractIpLink(String fileName, String savePath, ExtractContent extractContentValue){
		this.fileName = fileName;
		this.savePath = savePath;
		this.extractContentValue = extractContentValue;
	} 

	public void start(){
		Buffer buffer = new Buffer();
		Read read = new Read(this.fileName,buffer);
		Write write = new Write(this.savePath,buffer);
		Thread readThread1 = new Thread(read);
		Thread readThread2 = new Thread(read);
		Thread writeThread = new Thread(write);
		writeThread.setPriority(Thread.MIN_PRIORITY);
		
		readThread1.start();
		readThread2.start();
		writeThread.start();
	}
}

/*********************************************************************************
 * 
 * 缓冲池
 *
 *********************************************************************************
 */
class Buffer{
	private static String str = new String();
	protected static int isFinish = 1;
	private static int num = 0;
	private int srtSize = 1024*10;
	//private int srtSize = 1024*1024*10;
	private String retStr = new String();
	private boolean lock = true;
	Buffer(){
		
	}
	
	/**
	 * @name push
	 * @description 写入Buffer数据，当this.str.length()大于this.strSize时，阻塞写入进程
	 * @param pushStr
	 */
	public synchronized void push(String pushStr, boolean isFileEnd){
		if(!isFileEnd){
			D.p("合并前"+this.str+"@@@@");
			this.str +=pushStr;
			D.p("写入次数"+(++Buffer.num));
			D.p("合并后"+this.str+"@@@@");
			if(this.lock){
				if(this.str.length() >= this.srtSize){
					try {
						this.lock = false;
						D.p(Thread.currentThread().toString()+"lock1"+this.lock);
						this.notifyAll();//唤醒读取进程
						this.wait();//阻赛写入线程
						
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}else{
				try {
					D.p(Thread.currentThread().toString()+"lock2"+this.lock);
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}else{
			D.p(Thread.currentThread().toString()+"lock3"+isFileEnd);
			this.isFinish = this.isFinish<<1;
			D.p("isFinish"+this.isFinish);
			this.notifyAll();
		}
	}
	
	/**
	 * @name pop
	 * @description 读取Buffer中的数据 
	 * @return String
	 */
	public synchronized String pop(){
		if(this.isFinish != 4 ){
			D.p("我睡了3");
			if(this.str.length() < this.srtSize){
				try {
					D.p("我睡了2");
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}else{
				D.p(this.str.length()+"@"+this.str+"@");
				D.p("我睡了4");
				this.retStr = this.str;
				this.str = "";
				this.lock = true;
				this.notifyAll();
			}
			D.p("我睡了5");
			D.p(this.retStr);
		}else{
			D.p(Thread.currentThread().toString()+"lock4"+isFinish);
			this.retStr = this.str;
			this.isFinish = this.isFinish<<1;
		}
		return this.retStr;
	}
}
/*********************************************************************************
 * 
 * 读取文件操作
 *
 *********************************************************************************
 */
class Read implements Runnable{
	private String fileName = null;//源文件路径
	private Buffer buffer = null;//缓冲池对象
	private ExtractContent extractContentValue = null;//文件输入格式
	private BufferedReader reader = null;//
	private Pattern p = null;//正则表达式
	private byte[] lock = new byte[0];//加锁
	
	Read(String fileName, Buffer buffer){
		this.fileName = fileName;
		this.buffer = buffer;
		this.extractContentValue = ExtractContent.LINK_ALL;
		try {
			this.initReader();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.p = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b");

	}
	Read(String fileName, Buffer buffer, ExtractContent extractContentValue){
		this.fileName = fileName;
		this.buffer = buffer;
		this.extractContentValue = extractContentValue;
		try {
			this.initReader();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.p = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b");
	}
	
	/**
	 * @throws IOException 
	 * @name initReader
	 * @description 
	 */
	private synchronized void initReader() throws IOException{
			File files = new File(this.fileName);
			this.reader = new BufferedReader(new FileReader(files));
			//this.line = this.reader.readLine();
	}
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public  void run() {
		// TODO Auto-generated method stub
		String lineStr = new String();
		try {
			synchronized (this.lock){
				lineStr = this.reader.readLine();
				//this.line = lineStr;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while(lineStr != null){
			D.p("读进程"+Thread.currentThread().toString());
			D.p(lineStr);
			this.buffer.push(this.extractIps(lineStr), false);
			try {
				synchronized (this.lock){
					lineStr = this.reader.readLine();
					//this.line = lineStr;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.buffer.push("",true);
		D.p("读进程"+Thread.currentThread().toString()+"over");
	}
		
	/**
	 * @name extractIps
	 * @description 在读取的一行字符串中提取IP地址，并构造边的关系
	 * @param line
	 * @param p
	 * @return
	 */
	private String extractIps(String line){
		String str = new String();
		line = line.trim();
		if(line.startsWith("T")){
			//System.out.println(line.trim());
			Matcher m = this.p.matcher(line);
			Long sourceIp, destinationIp, nextIp;
			//String sourceIp, destinationIp, nextIp;
			switch(this.extractContentValue){
			//获取路由每跳，且最后一条不可达
			case LINK_I:
				if(line.contains("I")){
					m.find();
					sourceIp = MathTool.ip2Int(m.group());
					m.find();
					destinationIp = MathTool.ip2Int(m.group());
					str = sourceIp+"\t";
					while(m.find()){
						nextIp = MathTool.ip2Int(m.group());
						str += nextIp+"\r\n"+nextIp+"\t"; 
					}
					str += destinationIp+"\r\n";
				}
				break;
			//获取路由每跳，且到达目的IP
			case LINK_C:
				if(line.contains("C")){
					m.find();
					sourceIp = MathTool.ip2Int(m.group());
					m.find();
					destinationIp =  MathTool.ip2Int(m.group());
					str = sourceIp+"\t";
					while(m.find()){
						nextIp = MathTool.ip2Int(m.group());
						str += nextIp+"\r\n"+nextIp+"\t"; 
					}
					str += destinationIp+"\r\n";
				}
				break;
			//获取路由每跳
			case LINK_ALL:
				m.find();
				sourceIp = MathTool.ip2Int(m.group());
				m.find();
				destinationIp = MathTool.ip2Int(m.group());
				str = sourceIp+"\t";
				while(m.find()){
					nextIp = MathTool.ip2Int(m.group());
					str += nextIp+"\r\n"+nextIp+"\t"; 
				}
				str += destinationIp+"\r\n";
				break;
			//只获取源IP和目的IP
			case SOURCE_DESTINATION:			
			default :
				m.find();
				sourceIp = MathTool.ip2Int(m.group());
				m.find();
				destinationIp = MathTool.ip2Int(m.group());
				str += sourceIp+"\t"+destinationIp+"\r\n";			
				break;
			}
		}
		D.p("#"+str+"#");
		return str;
	}
}
/*********************************************************************************
 * 
 * 写文件
 *
 *********************************************************************************
 */
class Write implements Runnable{
	private Buffer buffer = null;
	BufferedWriter writer = null;
	Write(String savePath, Buffer buffer){
		this.buffer = buffer;
		try {
			writer = new BufferedWriter(new FileWriter(savePath));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		// TODO Auto-generated method stub
		String str = null;
		do{
			D.p("写进程"+Thread.currentThread().toString());
			D.p("我睡了1");
			str = this.buffer.pop();
			D.p("POP#"+str+"#");
			try {
				this.writer.append(str);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}while(Buffer.isFinish <= 4);
		try {
			writer.flush();
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		D.p("写进程"+Thread.currentThread().toString()+"over");
	}	
}